#include "util/radix_tree/var_len_data_accessor.h"

#include <gflags/gflags.h>

namespace hawking {
namespace indexlib {

DEFINE_int32(attr_slice_bytes, 1024 * 1024, "bytes per slice of attr");
DEFINE_int32(attr_offset_slice_bytes, 16 * 1024, "bytes per slice of attr offset");
DEFINE_int32(slice_num_per_node, 32, "slice num per node");

bool VarLenDataIterator::HasNext() {
    if (-1 == cursor_) {
        return offset_->Size() > 0;
    }

    return cursor_ < offset_->Size() - 1;
}

void VarLenDataIterator::Next() {
    if (cursor_ == -1) {
        cursor_ = 0;
    } else {
        ++cursor_;
    }

    if (cursor_ >= offset_->Size()) {
        return;
    }

    //current_offset_ = (*offset_)[cursor_];
    offset_->Read(cursor_, current_offset_);
    current_data_= data_->Search(current_offset_);
}

void VarLenDataIterator::CurrentData(uint8_t*& data) {
    if (cursor_ == -1 || cursor_ >= offset_->Size()) {
        data = nullptr;
        return;
    }

    data = current_data_;
}

void VarLenDataIterator::CurrentOffset(uint64_t* offset) {
    if (cursor_ == -1 || cursor_ >= offset_->Size()) {
        *offset = 0;
        return;
    }

    *offset = current_offset_;
}

void VarLenDataAccessor::Init(Pool* pool) {
    char* buffer = (char*)pool->Allocate(sizeof(RadixTree));
    data_ = new (buffer) RadixTree(FLAGS_slice_num_per_node, FLAGS_attr_slice_bytes, pool);
    buffer = (char*)pool->Allocate(sizeof(TypedSliceList<uint64_t>));
    offsets_ =
        new (buffer) TypedSliceList<uint64_t>(
            FLAGS_slice_num_per_node, FLAGS_attr_offset_slice_bytes, pool);
}

// write 4bytes(len of content) + content. if content is empty, only 4bytes(len = 0)
bool VarLenDataAccessor::AppendData_(const std::string_view& value, uint64_t* offset) {
    uint8_t* buffer = data_->Allocate(value.size());
    if (!buffer) {
        return false;
    }

    if (value.data() != nullptr) {
        std::memcpy(buffer, value.data(), value.size());
    }
    append_data_size_ += value.size();
    *offset = data_->GetCurrentOffset() - value.size();
    return true;
}

bool VarLenDataAccessor::AppendValue(const std::string_view& value) {
    uint64_t current_offset = 0;
    if (AppendData_(value, &current_offset)) {
        offsets_->PushBack(current_offset);
        return true;
    }

    return false;
}

bool VarLenDataAccessor::ReadData(DocId doc_id, uint8_t*& data) const {
    if (unlikely(doc_id >= offsets_->Size())) {
        data = nullptr;
        return false;
    }

    uint64_t* offset = nullptr;
    offsets_->Read(doc_id, std::ref(offset));
    if (unlikely(nullptr == offset)) {
        return false;
    }
    data = data_->Search(*offset);
    if (unlikely(nullptr == data)) {
        return false;
    }

    return true;
}

bool VarLenDataAccessor::UpdateValue(DocId doc_id, const std::string_view& value) {
    if (doc_id >= offsets_->Size()) {
        return false;
    }

    uint64_t current_offset = 0;
    if (!AppendData_(value, &current_offset)) {
        return false;
    }

    (*offsets_)[doc_id] = current_offset;
    return true;
}

std::shared_ptr<VarLenDataIterator> VarLenDataAccessor::CreateDataIterator() const {
    return std::make_shared<VarLenDataIterator>(data_, offsets_);
}

}
}